package org.example.spring03tx.service.impl;

import org.example.spring03tx.bean.Book;
import org.example.spring03tx.dao.AccountDao;
import org.example.spring03tx.dao.BookDao;
import org.example.spring03tx.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    BookDao bookDao;

    @Autowired
    AccountDao accountDao;

    /*
    * 事务细节：
    * 1.transactionManager:事务管理器：控制事务的获取、提交、回滚
    *       底层默认使用JdbcTransactionManager事务管理器
    *       原理：
    *           1.事务管理器：TransactionManager:控制提交和回滚
    *           2.事务拦截器：TransactionInterceptor:控制何时提交和回滚
    *               completeTransactionAfterThrowing(txInfo,ex):在这个时候回滚
    *               commitTransactionAfterReturning(txInfo):在这个时候提交
    * 2.propagation：传播行为 【注意关注异常传播链】
    * 3.isolation：隔离级别
    * 3.timeout（同timeoutString 字符串）：超时时间：事务超时则回滚，秒为单位
    *           超时时间指：从方法开始，到最后一次数据库操作结束的时间
    * 4.readOnly：只读事务,=true时开启只读优化
    * 5.rollbackFor(同rollbackForClassName)：指明那些异常要回滚。不是所有异常都一定引起事务回滚
    * rollbackFor(类名.class,...)
    *   异常：
    *       运行时异常(unchecked exception【非受检异常】)
    *       编译时异常(checked exception【受检异常】)
    *   回滚默认机制：
    *       运行时异常：回滚
    *       编译时异常：不回滚
    * rollbackFor可以指定哪些异常需要回滚（回滚=运行时异常+指定回滚异常）
    * 6.noRollbackFor(同 noRollbackForClassName):指明哪些异常不需要回滚
    *       【不回滚=编译时异常+指定不回滚异常】
    * */


    /*场景：用户结账，炸了后，金额扣减回滚，库存不回滚
    * 实现：
    *       checkOut(){
    *           //自己的操作
    *           扣减金额； //REQUIRED
    *           扣减库存;  //REQUIRES_NEW   */

    @Transactional
//    @Transactional(timeout=3) //传播行为：参数设置项也会传播，如果小失误和大事务公用一个事务，小事务要按照大事务的设置，小事务自己的设置失效
    @Override
    public void checkOut(String username, Integer bookID, Integer buyNum)  {
        //查询图书信息
        Book book = bookDao.getBookById(bookID);
        //扣减余额(REQUIRED)
        accountDao.reduceBalance(username,book.getPrice().multiply(new BigDecimal(buyNum)));
        //测试timeout,模拟超时
        //Thread.sleep(3000);
        //扣减库存(REQUIRES_NEW)
        bookDao.updateBookStock(bookID,buyNum);

        int i=1/0;

    }
}


